package mango.web;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import mango.Mango;
import mango.functions.FuncString;
import mango.orm.OrmTypes;
import mango.orm.DBSessionFactory;
import mango.utils.Logger;

public abstract class Action {
    public static final String REQUEST_METHOD = "md";
    private static final ConcurrentHashMap<String, Class<?>> ACTION_CACHE = new ConcurrentHashMap<String, Class<?>>();
    private static final ConcurrentHashMap<String, Method> METHOD_CACHE = new ConcurrentHashMap<String, Method>();
    protected HttpServletRequest request;
    private HttpServletResponse response;
    protected String method;
    protected FormChain formChain;
    protected ArrayList<Info> infos = new ArrayList<Info>();
    private String result;
    protected Logger logger = Mango.getLog();

    public static Action getAction(String actionName) {
        try {
            Class<?> action = ACTION_CACHE.get(actionName);
            if (action == null && !FuncString.isBlank(actionName)) {
                action = Class.forName(Mango.getProperty(Mango.ACTION_PACKAGE) + actionName);
                if (!Action.class.isAssignableFrom(action)) {
                    Mango.getLog().error(Action.class.getName() + " is not Assignable From " + action.getName());
                    return null;
                }
                ACTION_CACHE.put(actionName, action);
            }
            return (Action)action.newInstance();
        }
        catch (Exception e) {
            Mango.getLog().error(e);
            return null;
        }
    }

    /**
     * service
     */
    public final boolean service(FormChain formChain) throws Exception {
        try {
            if (!this.validate()) {
                return false;
            }
            this.formChain = formChain;
            logger.info("PARAMETERS|" + this.formChain.toString());
            Method method = METHOD_CACHE.get(this.getClass().getName() + "." + this.method);
            if (method == null) {
                method = this.getClass().getDeclaredMethod(this.method);
                METHOD_CACHE.put(this.getClass().getName() + "." + this.method, method);
            }
            Object ret = method.invoke(this);
            if (ret == null || !(ret instanceof Boolean)) {
                return true;
            }
            boolean bool = Boolean.parseBoolean(ret.toString());
            return bool;
        }
        catch (InvocationTargetException e) {
            throw (Exception)e.getTargetException();
        }
        catch(Exception e) {
            Mango.getLog().error("no " + this.method + " function! do nothing!");
        }
        return false;
    }

    public void handleException(Exception e) {
        Mango.getLog().error("error in " + this.getMethod(), e);
    }

    public void addInfo(int type, String msg) {
        this.infos.add(InfoMaker.getInstance().generateInfo(type, msg));
    }

    public String getInfo() {
        return InfoMaker.getInstance().makeInfo(infos);
    }

    public void forward(String url) {
        try {
            DBSessionFactory.closeCurrentSession();
            Mango.getLog().debug("forward url : " + url);
            this.request.getRequestDispatcher(url).forward(this.request, this.response);
        }
        catch (Exception e) {
            throw new DispatchException(e);
        }
    }

    public void redirect(String url) {
        try {
            DBSessionFactory.closeCurrentSession();
            Mango.getLog().debug("redirect url : " + url);
            this.response.sendRedirect(url);
        }
        catch (IOException e) {
            throw new DispatchException(e);
        }
    }

    /**
     * validate
     * 
     * @return errorMsg
     */
    protected boolean validate() {
        if (FuncString.isBlank(this.method)) {
            Mango.getLog().debug("method is null, to execute");
            this.method = "execute";
        }
        return true;
    }

    public String getRequestParameter(String paramName) {
        return this.formChain.getRequestParameter(paramName);
    }

    public int getRequestInt(String param) {
        return OrmTypes.convert(int.class, this.getRequestParameter(param));
    }

    public double getRequestDouble(String param) {
        return OrmTypes.convert(double.class, this.getRequestParameter(param));
    }

    public Date getRequestDate(String param) {
        return OrmTypes.convert(Date.class, this.getRequestParameter(param));
    }

    public ArrayList<Integer> getRequestInts(String param) {
        ArrayList<String> params = this.getRequestParameters(param);
        if (params == null) {
            return null;
        }
        ArrayList<Integer> ret = new ArrayList<Integer>();
        for (String s : params) {
            ret.add(OrmTypes.convert(int.class, s));
        }
        return ret;
    }

    public ArrayList<Double> getRequestDoubles(String param) {
        ArrayList<String> params = this.getRequestParameters(param);
        if (params == null) {
            return null;
        }
        ArrayList<Double> ret = new ArrayList<Double>();
        for (String s : params) {
            ret.add(OrmTypes.convert(double.class, s));
        }
        return ret;
    }

    public ArrayList<Date> getRequestDates(String param) {
        ArrayList<String> params = this.getRequestParameters(param);
        if (params == null) {
            return null;
        }
        ArrayList<Date> ret = new ArrayList<Date>();
        for (String s : params) {
            ret.add(OrmTypes.convert(Date.class, s));
        }
        return ret;
    }

    public ArrayList<String> getRequestParameters(String paramName) {
        return this.formChain.getRequestParameters(paramName);
    }

    public InputStream getRequestUploadStream(String paramName) {
        return this.formChain.getRequestUploadStream(paramName);
    }

    public ArrayList<InputStream> getRequestUploadStreams(String paramName) {
        return this.formChain.getRequestUploadStreams(paramName);
    }

    public Action putExtra(String key, Object value) {
        this.formChain.putExtra(key, value);
        return this;
    }

    public Object getExtra(String key) {
        return this.formChain.getExtra(key);
    }

    public Object getSessionAttr(String key) {
        return this.request.getSession().getAttribute(key);
    }

    public void setSessionAttr(String key, Object value) {
        this.request.getSession().setAttribute(key, value);
    }

    public HttpServletRequest getRequest() {
        return this.request;
    }

    public void setRequest(HttpServletRequest request) {
        this.request = request;
    }

    public HttpServletResponse getResponse() {
        return this.response;
    }

    public void setResponse(HttpServletResponse response) {
        this.response = response;
    }

    public String getResult() {
        return result;
    }

    public void setResult(String result) {
        this.result = result;
    }

    public String getMethod() {
        return this.method;
    }

    public void setMethod(String method) {
        this.method = method;
    }
}
